#ifndef _NE_FINITE_STATE_MACHINE_H_
#define _NE_FINITE_STATE_MACHINE_H_

#include <common/State.h>

namespace ne
{
    template<typename T> class FiniteStateMachine
    {
        public:
            explicit FiniteStateMachine(T *pContent)
                : mpContent(pContent),
                  mpCurrentState(0)
            {
            }

            const State<T>& getCurrentState() const
            {
                return *mpCurrentState;
            }

            uint32_t getCurrentStateID() const
            {
                if (mpCurrentState)
                {
                    return mpCurrentState->getID();
                }
                return FINITE_STATE_MACHINE_DEFAULT_STATE_ID;
            }

            // TODO: Call it for user to forcibly reset FSM
            //       and wont call _exit() for current state
            // TODO: If you write your custom derived DefaultState
            //       you must call reset() as TestFSM
            //       or the first state of the FSM will be the base DefaultState
            void reset()
            {
                delete mpCurrentState;
                mpCurrentState = createDefaultState();
                if (mpCurrentState)
                {
                    Event event("EnterDefaultStateEvent");
                    mpCurrentState->_enter(event, mpCurrentState->getID());
                }
            }

            // TODO: Call it for user to change the current state of the FSM
            //       Return true if the state is changed or loop successfully
            // TODO: The state will change to DefaultState
            //       when state change fail
            bool changeCurrentState(const uint32_t stateID, Event &event)
            {
                if (mpCurrentState)
                {
                    uint32_t currentStateID = mpCurrentState->getID();
                    if (currentStateID == stateID)
                    {
                        if (mpCurrentState->_loop(event))
                        {
                            return true;
                        }
                    }
                    else
                    {
                        if (mpCurrentState->_exit(event, stateID))
                        {
                            delete mpCurrentState;
                            mpCurrentState = 0;
                            mpCurrentState = createState(stateID);
                            if (mpCurrentState)
                            {
                                if (mpCurrentState->_enter(event, currentStateID))
                                {
                                    return true;
                                }
                            }
                        }
                    }
                }

                // TODO: If change current state fail
                //       reset the FSM and return false
                reset();
                return false;
            }

            // TODO: Call it for the FSM to deal with the event in formal way
            //       Return true if the current state has changed
            bool onEvent(Event &event)
            {
                if (mpCurrentState)
                {
                    uint32_t stateID = mpCurrentState->_event(event);
                    if (stateID != mpCurrentState->getID())
                    {
                        changeCurrentState(stateID, event);
                        return true;
                    }
                    return false;
                }

                reset();
                return false;
            }

            // TODO: Interface for user to create custom states
            virtual State<T>* createState(const uint32_t stateID)
            {
                return createDefaultState();
            }

            // TODO: Interface for user to create custom DefaultState
            virtual State<T>* createDefaultState()
            {
                return new State<T>(mpContent);
            }

            virtual ~FiniteStateMachine()
            {
                delete mpCurrentState;
            }

        private:
            FiniteStateMachine();

            FiniteStateMachine(const FiniteStateMachine &FSM);

            FiniteStateMachine& operator = (const FiniteStateMachine &FSM);

        protected:
            T *mpContent;
            State<T> *mpCurrentState;
    };
}

#endif
